<?php defined('SYS_PATH') or die('No direct script access.');
/**
 * Loader class
 *
 * @author zendzhang<zendzhang@hotmail.com>
 * @version $Id: Loader.php 9 2013-01-14 03:24:50Z zendzhang@hotmail.com $
 */
class TrueBlood_Loader
{
    /**
     * Array of loaded Files
     *
     * @var array
     */
    protected static $_loaded = array();

    /**
     * Array of loaded objects
     *
     * @var array
     */
    protected static $_objects = array();

    /**
     * Singleton instance
     *
     * @var object
     */
    protected static $_instance;

    /**
     * Constructor
     */
    protected function __construct()
    {
    }

    /**
     * Returns an instance of this class
     *
     * @return object
     */
    public static function &instance()
    {
        if (self::$_instance === null)
        {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * Finds the path of a file by directory and filename.
     *
     * @param string $dir
     * @param string $file
     * @param bool $both
     * @return mixed
     */
    public static function findFile($dir, $filename)
    {
        $file = trim($dir, '\\/') . '/' . $filename;
        if (isset(self::$_loaded[$file]))
        {
            return self::$_loaded[$file];
        }
        $realpath = realpath($file);
        if ($realpath && is_file($realpath))
        {
            self::$_loaded[$file] = $realpath;
            return $realpath;
        }
        return self::$_loaded[$file] = false;
    }

    /**
     * Load class
     *
     * @param string $className
     * @param string $classDir
     * @return bool
     */
    public static function loadClass($className, $classDir = null)
    {
        $className = ucfirst($className);
        if (class_exists($className, false) || interface_exists($className, false))
        {
            return true;
        }
        $classDir = trim($classDir, '\\/');
        if ('TrueBlood' === substr($className, 0, 9))
        {
            $classFile = ucfirst(substr($className, 10)) . '.php';
            $classDir = rtrim(SYS_PATH . '/' . $classDir, '/');
            $classPath = self::findFile($classDir, $classFile);
        }
        else
        {
            $elements = explode('_', $className);
            $classFile = ucfirst(array_pop($elements)) . '.php';
            $classDir = rtrim(APP_PATH . '/' . $classDir, '/');
            $classDir .= (count($elements) > 0) ? '/' . implode('/', $elements) : '';
            if (!($classPath = self::findFile($classDir, $classFile)))
            {
                $classPath = self::findFile($classDir, '_' . $classFile);
            }
        }
        if ($classPath)
        {
            require $classPath;
            return class_exists($className, false) || interface_exists($className, false);
        }
        return false;
    }

    /**
     * Factory method,Instantiate object by class name
     *
     * @param string $className
     * @throws Exception
     * @return object
     */
    public static function factory($className/*,Arguments...*/)
    {
        $args = func_get_args();
        $key = md5(serialize($args));
        if (isset(self::$_objects[$key]))
        {
            return self::$_objects[$key];
        }
        $object = call_user_func_array(array('self', 'newInstance'), $args);
        return self::$_objects[$key] = $object;
    }

    /**
     * Creates a new instance by class name
     *
     * @param string $className
     * @throws Exception
     * @return object
     */
    public static function newInstance($className/*,Arguments...*/)
    {
        $elements = explode('.', $className);
        $className = ucfirst(array_pop($elements));
        $classDir = (count($elements) > 0) ? implode('/', $elements) : '';
        if (!self::loadClass($className, $classDir))
        {
            $default = 'TrueBlood_' . $className;
            if (!self::loadClass($default, $classDir))
            {
                throw new TrueBlood_Exception('The class ' . $className . ' does not exist.');
            }
            $className = $default;
        }
        if (func_num_args() > 1)
        {
            $args = array_slice(func_get_args(), 1);
            $reflection = new ReflectionClass($className);
            return call_user_func_array(array(&$reflection, 'newInstance'), $args);
        }
        return new $className();
    }

    /**
     * Model Loader
     *
     * @access public
     * @param string $name
     * @return object
     */
    public static function model($name)
    {
        return self::factory('models.' . $name . 'Model');
    }

    /**
     * Model Loader
     *
     * @access public
     * @param string $name
     * @return object
     */
    public static function library($name, $config = null)
    {
        return self::factory('libraries.' . $name, $config);
    }

    /**
     * Database Loader
     *
     * @access public
     * @param string $name
     * @return object
     */
    public static function database($name = 'defalut')
    {
        return 'defalut' === $name ? self::factory('Database') : self::factory('Database')->instance($name);
    }

    /**
     * Gets config values
     *
     * @param string $file
     * @param array $default
     * @return array
     */
    public static function config($file, $default = array())
    {
        $loader = self::instance();
        try
        {
            $loader->config->load($file);
        }
        catch (TrueBlood_Exception $e)
        {
            $loader->logger->debug($e->getMessage());
            return $default;
        }
        return $loader->config->item($file, $default);
    }

    /**
     * Dynamic get object
     *
     * @param string $key
     */
    public function __get($key)
    {
        try
        {
            return $this->$key = self::factory($key);
        }
        catch (TrueBlood_Exception $tbe)
        {
            throw $tbe;
        }
        catch (Exception $e)
        {
            throw new TrueBlood_Exception('Undefined property: ' . get_class($this) . '::' . $key . '|Exception: ' . $e->getMessage());
        }
    }
}

?>